import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * 
 */

/**
 * @author Leena
 *
 */
public class VendingMachineTest {
		// The number of slots in the vending machine
		public static final int NUM_SLOTS = 4;

		// The code for the "A" slot
		public static final String A_CODE = "A";

		// The code for the "B" slot
		public static final String B_CODE = "B";
		
		// The code for the "C" slot
		public static final String C_CODE = "C";
		
		// The code for the "D" slot
		public static final String D_CODE = "D";

		
		VendingMachine vm;
		VendingMachineItem vmItem1;
		VendingMachineItem vmItem2;
		VendingMachineItem vmItem3;
		VendingMachineItem vmItem4;
	/**
	 * @throws java.lang.Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	/**
	 * @throws java.lang.Exception
	 */
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		vm = new VendingMachine();
		vmItem1 = new VendingMachineItem("Tea", 1.15);
		vmItem2 = new VendingMachineItem("Coffee", 1.15);
		vmItem3 = new VendingMachineItem("Hot chocolate", 1.25);
		vmItem4 = new VendingMachineItem("Water", 1);
		
		
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 */
	@Test
	public void testAddItem() {
		vm.addItem(vmItem1,"A");
		vm.addItem(vmItem2,"B");
		vm.addItem(vmItem3,"C");
		vm.addItem(vmItem4,"D");
		
		VendingMachineItem item1 = vm.getItem("A"); //retrieving items from vending machine
		VendingMachineItem item2 = vm.getItem("B");
		VendingMachineItem item3 = vm.getItem("C");
		VendingMachineItem item4 = vm.getItem("D");
		
		assertEquals("Tea", item1.getName()); //test if names of the items retrieved are the same as inserted values after items are added
		assertEquals("Coffee", item2.getName()); 
		assertEquals("Hot chocolate", item3.getName());
		assertEquals("Water", item4.getName());
		
		assertEquals(1.15, item1.getPrice(),0); //test if prices of the items retrieved are the same as inserted values after items are added
		assertEquals(1.15, item2.getPrice(), 0);
		assertEquals(1.25, item3.getPrice(), 0);
		assertEquals(1, item4.getPrice(), 0);
	}

	@Test (expected = VendingMachineException.class)
	public void testAddItemException(){
		vm.addItem(vmItem1, "A"); 		
		vm.addItem(vmItem1, "A"); //test if program go through exception when an item is inserted to the occupied slot
	}
	
	@Test (expected = VendingMachineException.class)
	public void testAddItemException2(){
		vm.addItem(vmItem1, "E"); 		//test if program go through exception when an item is inserted to an unkown slot
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 */
	@Test
	public void testRemoveItem() {
		vm.addItem(vmItem1, "A");
		VendingMachineItem item = vm.removeItem("A");
		
		assertEquals("Tea", item.getName()); //test if name of returned item from testRemoveItem is the same as expected value
		assertEquals(1.15, item.getPrice(), 0); //test if price of returned item from testRemoveItem is the same as expected value
		
		assertEquals(null, vm.getItem("A")); //test if the slot of itemArray in which item existed is empty after item is removed
	}

	//Tests If removes item from a slot given an invalid code 
		@Test (expected=VendingMachineException.class)
		 public void testRemoveItem_invalidCode (){
			vm.removeItem("E");
			assertEquals(null,vm.getItem("E"));
		}
			
		//Tests If removes item from a slot already empty
		@Test (expected=VendingMachineException.class)
		public void testRemoveItem_empty (){
			//adding item to a slot
			vm.addItem(vmItem1, "A");
			//removing item from a slot
			vm.removeItem("A");
			//removing from a slot that is already empty
			vm.removeItem("A");
			assertEquals(null,vm.getItem("A"));
		}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 */
	@Test
	public void testInsertMoney() {
		vm.insertMoney(2);
		vm.insertMoney(1.5);
		assertEquals(3.5, vm.getBalance(), 0); //test if balance of vendingMachine is the same as inserted money
	}

	 //Tests if it works when an invalid amount(less than zero) is inserted
	 @Test (expected=VendingMachineException.class)        
	 public void testInsertMoney_invalid() {
		 vm.insertMoney(-1.0); 
		 assertEquals(-1.0,vm.balance,0);          
	}
	 
	//Tests if it works when an amount is equal to zero is inserted
	@Test        
	public void testInsertMoney_zeroPrice() {  
		vm.insertMoney(0.0); 
		double balance=0.0;
		balance= balance + 0.0;
		assertEquals(balance,vm.balance,0);         
	}     
	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 */
	@Test
	public void testGetBalance() {
		vm.insertMoney(2.15);
		assertEquals(2.15, vm.getBalance(), 0); //test amount of balance after money is inserted
		
		vm.addItem(vmItem1, "A");
		vm.makePurchase("A");
		assertEquals(1, vm.getBalance(), 0); //test amount of balance after makePurchase
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 */
	@Test
	public void testMakePurchase() {
		vm.addItem(vmItem1, "A"); //add 2 items 
		vm.addItem(vmItem3, "C");
		
		vm.insertMoney(1.20);
		
		assertEquals(true, vm.makePurchase("A")); //test if makePurchase returns true when item != null and balance >= price
		assertEquals(false, vm.makePurchase("C")); //test if makePurchase returns true when item != null and balance < price
		assertEquals(false, vm.makePurchase("D")); //test if makePurchase returns true when item == null and balance < price
	}

	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 */
	@Test
	public void testReturnChange() {
		vm.insertMoney(2.5);
		
		assertEquals(2.5, vm.returnChange(), 0); //test if returnChange() returns correct change after money is inserted
		
		vm.addItem(vmItem1, "A");
		vm.insertMoney(2.5);
		vm.makePurchase("A");
		
		assertEquals(1.35, vm.returnChange(), 0); //test if returnChange() returns correct change after makePurchase()
	}
	
	 
	//Tests if it works when money less than the price is inserted
	 @Test          
	 public void testMakePurchase_lessMoney() {
		 vm.addItem(vmItem1, "A");
		 vm.insertMoney(.50);  
		 assertFalse(vm.makePurchase("A"));          
	} 
	 
	//Tests if it works when money less than the price is inserted
	 @Test          
	 public void testMakePurchase_emptySlot() {
		 vm.addItem(vmItem1, "A");
		 vm.removeItem("A");
		 vm.insertMoney(2.00);  
		 assertFalse(vm.makePurchase("A"));          
	} 
	 
 
	//Tests if it works when there is no change to return
	 @Test          
	 public void testReturnChange_noChange() {                  
		 vm.addItem(vmItem1, "A");                                    
		 vm.insertMoney(1.15); 
		 vm.makePurchase("A");       
		 assertEquals(0.00, vm.returnChange(),0);          
	}

}
